/**
 * 
 */
package server;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;

import org.apache.log4j.Logger;

import server.alarmThreads.AlarmappAlarmierer;
import server.alarmThreads.FMSSocketConnctor;
import server.alarmThreads.OnlineCheckerThread;
import server.alarmThreads.PocsagUeberwacher;
import server.alarmThreads.UnifiedAlarmThread;
import server.config.ServerConfig;
import server.container.DubBlocker;
import server.container.EinsatzAlarm;
import server.container.LocalAlarm;
import server.container.UserAlarm;
import server.container.UserBlocker;
import server.db.SQL;

/**
 * @author Christian
 * 
 */
public class ServerAlarmAppController implements Runnable {

	/**
	 * 
	 */
	private BlockingQueue<EinsatzAlarm> rawEinsatzQueue = new LinkedBlockingQueue<EinsatzAlarm>();

	private ConcurrentLinkedQueue<UserAlarm> userAlarmList = new ConcurrentLinkedQueue<UserAlarm>();

	private Semaphore userAlarmFuellstand = new Semaphore(0);

	private List<UnifiedAlarmThread> alarmThreads = new ArrayList<>();

	private Map<String, UserBlocker> blockermap = new HashMap<String, UserBlocker>();

	private Map<String, DubBlocker> dubBlockerMap = new HashMap<>();
	
	private FMSSocketConnctor fms32Connector = null;

	private static Logger logger = Logger.getLogger(ServerAlarmAppController.class);

	public ServerAlarmAppController() {

		initThreads();
	}

	/**
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {

		logger.trace("Controllerrunmethode");
		while (true) {

			try {

				// Alarmverarbeitung

				EinsatzAlarm alarm;
				try {
					alarm = rawEinsatzQueue.take();
				} catch (InterruptedException e) {

					e.printStackTrace();
					continue;
				}
				
				logger.trace("Alarmverarbeiten beginn " + alarm.getSchleife());

				long t1 = System.nanoTime();
				
				// isalive aufrecherhalten
				OnlineCheckerThread.getInstance().updateLastAlarm();
				//Pocsagaktivität signalisieren
				if(alarm.isPocsag()) {				
					PocsagUeberwacher.pocsagKeepAlive();
				}

				// Skip keepalive und bekannte fehlerhafte
				if (isOnSkipList(alarm.getSchleife(), alarm.getStatus())) {

					SQL.getInstance().getConPool().connectionOk();
					continue;
				}
				
				logger.trace("Alarmverarbeitung mitte");

				// Dubbletten Blocker
				if (dubBlockerMap.containsKey(alarm.getSchleife())) {
					if (dubBlockerMap.get(alarm.getSchleife()).isBlockAktuell()) {
						continue;
					} else {
						dubBlockerMap.remove(alarm.getSchleife());
					}
				}
				long blockdauer = 1000 * 90;
				dubBlockerMap.put(alarm.getSchleife(), new DubBlocker(alarm.getSchleife(), blockdauer));

				// Alarm in DB schreiben
				if (!alarm.isIstInDBGespeichert()) {
					SQL.alarmierungInDBEintragen(alarm);
					alarm.setIstInDBGespeichert(true);
				}
				logger.trace("alarm in db gesichert");
				// 5ton alarme Pocsag verzögern
				if (alarm.is5Ton() && alarm.istFMSFeuerwehr()) {
					if (alarm.getAlarmTime() + 35000 > System.currentTimeMillis()) {

						try {
							rawEinsatzQueue.put(alarm);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						sleep(100);

						continue;
					}
				}

				// Blocker für 5-ton Wehren verkürzen
				// TODO
				// if (!alarm.istFMSFeuerwehr() && !alarm.is5Ton() &&
				// !alarm.isPocsag()) {
				// SQL.setIstFMSFeuerwehr(alarm.getFeuerwehrID());
				// }
				logger.trace("Alarmapp alarmieren");
				if(alarm.getFeuerwehrID() == 1 || alarm.getFeuerwehrID() == 2) { //Leuchtenburg
					
					AlarmappAlarmierer app = new AlarmappAlarmierer(alarm);
					app.alarmiere();
				}
				logger.trace("useralarme holen");
				List<UserAlarm> userAlarme = createUserAlarmList(alarm);
				logger.trace("useralarme geholt");

				if (userAlarme.size() == 0) {
					logger.info("Keine Alarme für diese Schleife! " + alarm.getSchleife());
				}

				for (UserAlarm ua : userAlarme) {

					if (!isBlocked(ua)) {
						addToBlockMap(ua);
						userAlarmList.add(ua);
						userAlarmFuellstand.release();
					}
				}

				long t2 = System.nanoTime();
				logger.info("Zeit:" + (t2 - t1) / 1000000 + " ms");

			} catch (Exception e) {
				logger.error("Exception in der Mainschleife", e);
			}

		}
	}

	private void addToBlockMap(UserAlarm ua) {
		long blockDauer = 1000 * 60 * 30;

		UserBlocker uBlocker = new UserBlocker(ua.getBlockIdentifier(), blockDauer, ua.isProbeAlarm());
		blockermap.put(ua.getBlockIdentifier(), uBlocker);

	}

	private List<UserAlarm> createUserAlarmList(EinsatzAlarm alarm) {

		List<UserAlarm> alarme = new ArrayList<>();
		alarme.addAll(SQL.getSMSAlarmList(alarm));
		alarme.addAll(SQL.getPushAlarmInfos(alarm));
		alarme.add(new LocalAlarm(-1, alarm));

		return alarme;
	}

	private boolean isBlocked(UserAlarm ua) {

		if (!blockermap.containsKey(ua.getBlockIdentifier())) {
			return false;
		}
		UserBlocker b = blockermap.get(ua.getBlockIdentifier());
		if (!b.isBlockAktuell() || (b.isProbeAlarm() && !ua.isProbeAlarm())) {

			if (!b.isBlockAktuell()) {
				blockermap.remove(ua.getBlockIdentifier());
			}
			return false;
		}

		return true;
	}

	private void initThreads() {
		
		

		logger.trace("Start Alarm-Threads");
		int anzahlThreads = ServerConfig.getInstance().getAlarmThreadsAnzahl();
		for (int i = 0; i < anzahlThreads; i++) {
			alarmThreads.add(new UnifiedAlarmThread(userAlarmList, userAlarmFuellstand));
		}
		for (UnifiedAlarmThread t : alarmThreads) {
			t.start();
		}
		
		fms32Connector = new FMSSocketConnctor(rawEinsatzQueue);
		Thread t = new Thread(fms32Connector);
		t.start();
		
		logger.info(anzahlThreads + " Alarm-Threads gestartet");
	}

	/**
	 * 
	 * @param sleep
	 *            Sleep
	 */
	private void sleep(long sleep) {

		try {
			Thread.sleep(sleep);
		} catch (InterruptedException e) {
			logger.error("", e);
		}
	}

	public BlockingQueue<EinsatzAlarm> getEinsatzList() {

		return rawEinsatzQueue;
	}

	private boolean isOnSkipList(String schleife, String status) {
		return schleife.equals("0576000") || schleife.equals("0846008") || schleife.equals("80-60") || schleife.equals("80-42") || (schleife.equals("00-00") && status.equals("0"));
	}
}
